<?php
/**
 * Injector class from which instances of classes are returned. 
 * Depending on the binding rules those instances will have injected dependencies.
 */
class IOC_Injector {
	private $classBindings = array();
	private $instanceBindings = array();
	private $providerBindings = array();
	
	private $bindings = array();
	/**
	 * @var IOC_Binder
	 */
	private $defaultBinder;
	
	/**
	 * @var IOC_Injector
	 */
	private static $instance = null;
	
	private function __construct() {
	    $this->defaultBinder = new IOC_TreeBinder();
	}
	
	/**
	 * Returns a singleton instance of the injector
	 * @return IOC_Injector
	 */
	public static function get() {
		if(is_null(self::$instance)) {
			self::$instance = new IOC_Injector();
		}
		return self::$instance;
	}
	
	/**
	 * Binds a class
	 * @param $className
	 * @return IOC_BinderFactory
	 */
	public function bind($className) {
		return new IOC_BinderFactoryImpl($className, $this);
	}
	
	
	/**
	 * Adds a binder to for a class name 
	 * @param $className
	 * @param $binder
	 */
	public function addBinder($className, IOC_Binder $binder) {
	    if(empty($className)) {
	        throw new IOC_BindingException("The class name is null or empty.");
	    }
	    if(is_null($binder)) {
	        throw new IOC_BindingException("The binder that you are trying to add is null.");
	    }
	    
		if(isset($this->bindings[$className])) {
			throw new IOC_BindingException("Binding for $className already exists.");
		}
		
		$this->bindings[$className] = $binder;
	}
	
	/**
	 * Set the default binder. It is the binder to use when no other binder was found.
	 * @param $binder
	 */
	public function setDefaultBinder(IOC_Binder $binder) {
	    if(is_null($binder)) {
	        throw new IOC_BindingException("The binder that you are trying to add is null.");
	    }
	    $this->defaultBinder = $binder;
	}
	
	/**
	 * Gets an instance of the specified class
	 * @param $className
	 * @return object
	 */
	public function getInstance($className) {
		if(isset($this->bindings[$className])) {
			$binder = $this->bindings[$className];
			return $binder->get($className);
		} elseif(!is_null($this->defaultBinder)) {
		    return $this->defaultBinder->get($className);
		} else {
			throw new IOC_BindingException("Injector couldn't find a binder for the required classname.");
		}
	}
}
?>